﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using Shared = Faust.Andrew.MTG.Player.Shared;
using System.Windows;


namespace Faust.Andrew.MTG.Player
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
    public class MTGServer : Shared.IMTGServer
    {
        private Faust.Andrew.MTG.Player.Shared.GameCard _currentPlane;
        private bool _isUsingPlanes = false;
        private object _lock;

        //Game State Properties
        protected bool _gameStarted;
        //protected Shared.GameState _gameState;
        protected List<int> _usedIds;
        protected Random _rand;

        Dictionary<int, PlayerState> _players;
        Dictionary<int, Shared.GameCard> _cards;

        private List<Shared.GameCard> _planes;
        private List<Shared.GameCard> _usedPlanes;

        public MTGServer(bool usePlanes)
        {
            _lock = new object();
            _rand = new Random();
            _usedIds = new List<int>();
            _usedIds.Add(0);
            _cards = new Dictionary<int, Shared.GameCard>();
            _players = new Dictionary<int,PlayerState>();
            _planes = new List<Faust.Andrew.MTG.Player.Shared.GameCard>();
            _usedPlanes = new List<Faust.Andrew.MTG.Player.Shared.GameCard>();
            _isUsingPlanes = usePlanes;
            AddPlanes();
        }        

        protected void AddPlanes()
        {
            if (!_isUsingPlanes) { return; }
            string[] lines = Resources.Planes.Split('\r');
            for (int i = 0; i < lines.Length; i++)
            {
                Shared.GameCard card = new Faust.Andrew.MTG.Player.Shared.GameCard()
                {
                    MultiverseID = lines[i].Trim(),
                    CardID = NextId(),
                    ControllerID = 0,
                    OwnerID = 0
                };

                _cards.Add(card.CardID, card);
                
                _planes.Add(card);
            }

            ServerHelper.Shuffle(_planes, _rand);
        }

        protected FaultException CreateFault(Exception e, string faultCode)
        {
            return new FaultException(new FaultReason(e.Message), new FaultCode(faultCode));
        }

        protected FaultException CreateFault(Exception e)
        {
            return CreateFault(e, "Game Server Exception");
        }

        protected int NextId()
        {
            int id = 0;
            while (_usedIds.Contains(id))
            {
                id = _rand.Next(1, Int32.MaxValue);
            }

            _usedIds.Add(id);

            return id;
        }

        private void AddDeck(PlayerState player, Deck deck)
        {
            foreach (DeckCard deckCard in deck.Cards)
            {
                for (int i = 0; i < deckCard.Quantity; i++)
                {
                    Shared.GameCard card = new Faust.Andrew.MTG.Player.Shared.GameCard()
                    {
                        MultiverseID = deckCard.Card.MultiverseID,
                        CardID = NextId(),
                        Name = deckCard.Card.Name,
                        ControllerID = player.PlayerID,
                        OwnerID = player.PlayerID,
                        Location = new Shared.Location(player.PlayerID, Faust.Andrew.MTG.Player.Shared.Zone.Deck)
                    };

                    player.AddCardToDeck(card);
                    _cards.Add(card.CardID, card);
                }
            }

            player.ShuffleDeck();
        }

        protected Shared.GameState ConstructGameState()
        {
            Shared.GameState gameState = new Faust.Andrew.MTG.Player.Shared.GameState();

            gameState.IsPlanechase = _isUsingPlanes;

            gameState.Cards = new List<Shared.GameCard>(_cards.Values.ToArray());

            foreach (int id in _players.Keys)
            {
                gameState.Players.Add(_players[id].CopyClientPlayerState());
            }

            if (_isUsingPlanes)
            {
                gameState.CurrentPlane = Planeswalk();
            }

            return gameState;
        }

        protected void RefreshGameStatePrivate(int playerId)
        {
            if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

            Shared.RefreshGameStateAction gameStateAction = new Faust.Andrew.MTG.Player.Shared.RefreshGameStateAction();
            gameStateAction.IsSystemGenerated = true;
            gameStateAction.GameState = ConstructGameState();

            foreach (PlayerState player in _players.Values)
            {                
                Enqueue(player.PlayerID, gameStateAction);
            }
        }

        protected void Enqueue(int playerId, Shared.Action action)
        {
            _players[playerId].EnqueueAction(action);
        }

        private void Enqueue(Shared.Action action)
        {
            foreach (PlayerState player in _players.Values)
            {
                Enqueue(player.PlayerID, action);
            }
        }

        #region IMTGServer Members        

        public int JoinGame(string playerName, Deck deck)
        {
            //lock (_lock)
            {                
                PlayerState playerState = _players.SingleOrDefault(p => p.Value.Name == playerName).Value;

                if (playerState != null)
                {
                    throw CreateFault(new Exception("Player has already registered a deck"));
                }
                else
                {
                    playerState = new PlayerState(_rand);
                    playerState.DeckName = deck.Name;
                    playerState.PlayerID = NextId();
                    playerState.Name = playerName;

                    _players.Add(playerState.PlayerID, playerState);
                    AddDeck(playerState, deck);
                }

                Notify(playerState.PlayerID, String.Format("{0} has joined the game", playerState.Name), true, Shared.NotificationType.Notification);

                return playerState.PlayerID;
            }            
        }

        public int RejoinGame(string playerName)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                PlayerState playerState = _players.SingleOrDefault(p => p.Value.Name == playerName).Value;

                if (playerState == null)
                {
                    throw CreateFault(new Exception("Player hasn't registered a deck yet."));
                }
                else
                {
                    Shared.RefreshGameStateAction action = new Faust.Andrew.MTG.Player.Shared.RefreshGameStateAction();
                    action.PlayerID = playerState.PlayerID;
                    action.IsSystemGenerated = true;
                    action.GameState = ConstructGameState();

                    Enqueue(action.PlayerID, action);

                    Notify(playerState.PlayerID, String.Format("{0} has rejoined the game", playerState.Name, true));

                    return playerState.PlayerID;
                }
            }
        }

        public void StartGame()
        {
            //lock (_lock)
            {
                _gameStarted = true;

                List<int> used = new List<int>();

                if (_players.Count == 1)
                {
                    int tempId = NextId();
                    _players.Add(tempId, new PlayerState(_rand) { Name = "Computer", PlayerID = tempId });
                }

                foreach (int id in _players.Keys)
                {
                    int tempOrder = _rand.Next(0, _players.Count);
                    while (used.Contains(tempOrder))
                    {
                        tempOrder = _rand.Next(0, _players.Count);
                    }

                    used.Add(tempOrder);
                    _players[id].Order = tempOrder;                    
                }

                Notify(0, "Game is starting", true, Shared.NotificationType.Notification);
                
                RefreshGameStatePrivate(0);                    
            }
        }

        public void EndGame()
        {
            //lock (_lock)
            {
                _gameStarted = false;
                _players = new Dictionary<int, PlayerState>();
                _cards = new Dictionary<int, Shared.GameCard>();
            }
        }

        protected Shared.GameCard Planeswalk()
        {
            //Planeswalk
            if (_planes.Count == 0)
            {
                _planes.AddRange(_usedPlanes);
                _usedPlanes.Clear();
                ServerHelper.Shuffle(_planes, _rand);
            }

            Shared.GameCard planeCard = _planes[0];
            _planes.Remove(planeCard);
            _usedPlanes.Add(planeCard);

            //There may be counters on it from last time it was in play. If so we need to clear them.
            planeCard.Counters = new System.Collections.ObjectModel.ObservableCollection<Faust.Andrew.MTG.Player.Shared.Counter>();

            _currentPlane = planeCard;

            return planeCard;
        }

        public void AttemptPlaneswalk(int playerId)
        {
            //lock (_lock)
            {                
                if (!_gameStarted || !_isUsingPlanes) { return; }
                int roll = _rand.Next(1, 7);
                if (roll == 1)
                {
                    Shared.GameCard planeCard = Planeswalk();

                    Shared.PlanesWalkAction action = new Faust.Andrew.MTG.Player.Shared.PlanesWalkAction()
                    {
                        PlayerID = playerId,
                        IsSystemGenerated = false,
                        CardID = planeCard.CardID
                    };

                    Shared.UpdateCardAction updateCardAction = new Faust.Andrew.MTG.Player.Shared.UpdateCardAction()
                    {
                        PlayerID = playerId,
                        IsSystemGenerated = true,
                        CardID = planeCard.CardID,
                        Card = planeCard
                    };


                    Notify(playerId, "Successfully Planeswalked", false, Shared.NotificationType.PriorityNotification);

                    Enqueue(updateCardAction);
                    Enqueue(action);
                }
                else if (roll == 6)
                {
                    Notify(playerId, "Rolled Chaos", false, Shared.NotificationType.PriorityNotification);
                }
                else
                {
                    Notify(playerId, "Failed to Planeswalk", false, Shared.NotificationType.PriorityNotification);
                }
            }
        }

        public Queue<Shared.Action> Poll(int playerId)
        {
            //lock (_lock)
            {
                //if (!_gameStarted) { return new Queue<Faust.Andrew.MTG.Player.Shared.Action>(); }

                return _players[playerId].AllActions;
            }
        }

        public void SendMessage(int playerId, List<int> targetPlayersIds, string message)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                //foreach (int targetPlayerId in targetPlayersIds)
                //{
                //    Shared.MessageAction action = new Faust.Andrew.MTG.Player.Shared.MessageAction();
                //    action.PlayerID = playerId;
                //    action.Message = message;

                //    Enqueue(targetPlayerId, action);
                //}
                Shared.NotificationAction action = new Faust.Andrew.MTG.Player.Shared.NotificationAction();
                action.PlayerID = playerId;                
                action.Notification.Message = message;
                action.Notification.NotificationType = Faust.Andrew.MTG.Player.Shared.NotificationType.Message;
                action.IsSystemGenerated = false;

                foreach (int targetPlayerId in targetPlayersIds)
                {
                    Enqueue(targetPlayerId, action);
                }
            }
        }

        public void ShowCards(int playerID, List<int> targetPlayersIDs, List<int> cardIds)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                List<Shared.GameCard> cards = new List<Faust.Andrew.MTG.Player.Shared.GameCard>();
                foreach (int cardId in cardIds)
                {
                    cards.Add(_cards[cardId]);
                }

                foreach (int targetPlayerId in targetPlayersIDs)
                {
                    Shared.ShowCardsAction action = new Faust.Andrew.MTG.Player.Shared.ShowCardsAction();
                    action.PlayerID = playerID;
                    action.Cards = cards;

                    Enqueue(targetPlayerId, action);
                }
            }
        }

        public List<Shared.GameCard> GetCardList(int playerId, int targetPlayerId, Shared.Zone zone)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                List<Shared.GameCard> returnCards = new List<Faust.Andrew.MTG.Player.Shared.GameCard>();

                //If it's the deck that was requested we only show it it if's the current player's deck
                //or the deck has been unlocked.
                if (zone == Faust.Andrew.MTG.Player.Shared.Zone.Deck 
                    && (_players[targetPlayerId].IsDeckUnlocked || targetPlayerId == playerId))
                {
                    returnCards = _players[targetPlayerId].Deck;
                    //Tell everyone that the deck is being viewed.
                    Notify(playerId, String.Format("Is Viewing {0}'s Deck", _players[targetPlayerId].Name));
                }
                else if (zone == Faust.Andrew.MTG.Player.Shared.Zone.Graveyard || targetPlayerId == playerId)
                {
                    var cards = from card in _cards
                                where card.Value.Location.PlayerID == targetPlayerId
                                && card.Value.Location.Zone == zone
                                select card.Value;

                    foreach (var card in cards)
                    {
                        returnCards.Add(card);
                    }
                }

                return returnCards;
            }
        }

        public void Notify(int playerId, string message)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                Notify(playerId, message, false, Faust.Andrew.MTG.Player.Shared.NotificationType.Notification);
            }
        }

        private void Notify(int playerId, string message, bool isSystemGenerated, Shared.NotificationType notificationType)
        {
            //foreach (Shared.Player player in _players.Values)
            //{
            Shared.NotificationAction action = new Faust.Andrew.MTG.Player.Shared.NotificationAction();            
            action.PlayerID = playerId;

            if (_players.ContainsKey(playerId))
            {
                action.Notification.Message = String.Format("{0}: {1}", _players[playerId].Name, message);
            }
            else
            {
                action.Notification.Message = message;
            }

            action.Notification.NotificationType = notificationType;
            action.IsSystemGenerated = isSystemGenerated;

            Enqueue(action);            
            //}
        }

        public void RefreshGameState(int playerId)
        {
            //lock (_lock)
            {
                RefreshGameStatePrivate(playerId);
            }
        }

        public bool UpdateCard(int playerID, Shared.GameCard card)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                string updateText = String.Empty;

                Shared.GameCard updatedCard;

                if (_currentPlane != null && card.CardID == _currentPlane.CardID)
                {                    
                    //The only update possible on the Plane cards are counters.
                    if (ServerHelper.AreCountersUpdated(_currentPlane.Counters, card.Counters))
                    {
                        _currentPlane.Counters = card.Counters;
                        updateText = "Counters have been changed on Plane";
                    }

                    updatedCard = _currentPlane;
                }
                else
                {
                    updatedCard = _players[playerID].UpdateCard(card, ref updateText);
                }

                if (updatedCard == null)
                {
                    return false;
                }

                //Send the change to all the players
                //foreach (PlayerState player in _players.Values)
                //{
                    Shared.UpdateCardAction action = new Faust.Andrew.MTG.Player.Shared.UpdateCardAction();
                    action.PlayerID = playerID;
                    action.CardID = card.CardID;
                    action.Card = card;

                    Enqueue(action);
                //}

                    Notify(playerID, updateText, false, Shared.NotificationType.Notification);

                return true;                          
            }
        }        

        public bool MarkCard(int playerId, int cardId, bool isMarked)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                Shared.MarkCardAction action = new Faust.Andrew.MTG.Player.Shared.MarkCardAction();
                action.PlayerID = playerId;
                action.CardID = cardId;
                action.Marked = isMarked;

                //We will only send the "Mark" to the controller of the card.
                Enqueue(_cards[cardId].ControllerID, action);

                return true;
            }
        }

        public void RemoveTokenCreature(int playerId, int cardId)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                Shared.GameCard card = _cards[cardId];
                PlayerState player = _players[card.Location.PlayerID];

                if (playerId != card.ControllerID) { return; }

                player.RemoveCard(card);

                //foreach (PlayerState tempPlayer in _players.Values)
                //{
                    Shared.RemoveTokenAction action = new Faust.Andrew.MTG.Player.Shared.RemoveTokenAction();
                    action.CardID = card.CardID;
                    action.PlayerID = player.PlayerID;

                    Enqueue(action);
                //}

                Notify(card.ControllerID, "Token Creature Removed", false, Faust.Andrew.MTG.Player.Shared.NotificationType.Notification);
            }
        }

        public void PlayTokenCreature(int playerId, string tokenText, string imageKey, int quantity)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }    

                PlayerState player = _players[playerId];

                Notify(playerId, String.Format("Played {0} {1} tokens", quantity, tokenText), false, Shared.NotificationType.Notification);

                for (int i = 0; i < quantity; i++)
                {                                   
                    Shared.GameCard card = new Faust.Andrew.MTG.Player.Shared.GameCard()
                    {
                        MultiverseID = String.Empty,
                        CardID = NextId(),
                        ControllerID = player.PlayerID,
                        OwnerID = player.PlayerID,
                        TokenImageKey = imageKey,
                        Location = new Shared.Location()
                        {
                            PlayerID = player.PlayerID,
                            Zone = Faust.Andrew.MTG.Player.Shared.Zone.InPlay,
                            X = 0,
                            Y = 0,
                            Z = 0
                        },
                        ModifierText = tokenText,
                        IsFaceDown = false,
                        IsToken = true
                    };

                    _cards.Add(card.CardID, card);

                    //foreach (PlayerState tempPlayer in _players.Values)
                    //{
                        Shared.MoveCardAction action = new Faust.Andrew.MTG.Player.Shared.MoveCardAction();
                        action.Card = card;
                        action.CardID = card.CardID;
                        action.PlayerID = player.PlayerID;

                        Enqueue(action);
                    //}

                    player.AddCard(card);
                }
            }
        }

        public bool SendCardToDeck(int playerId, int cardId, Shared.DeckPosition deckPosition, int offset)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                Shared.GameCard card = _cards[cardId];
                if (playerId != card.ControllerID) { return false; }

                Shared.Zone originalZone = card.Location.Zone;

                PlayerState player = _players[playerId];

                player.AddCardToDeck(card, deckPosition, offset);

                foreach (PlayerState tempPlayer in _players.Values)
                {
                    if (tempPlayer.PlayerID == playerId) { continue; }

                    Shared.MoveCardAction action = new Faust.Andrew.MTG.Player.Shared.MoveCardAction();
                    action.PlayerID = playerId;
                    action.CardID = cardId;
                    action.Card = card;

                    Enqueue(tempPlayer.PlayerID, action);
                }

                string message = "";
                if (deckPosition == Faust.Andrew.MTG.Player.Shared.DeckPosition.FromTop)
                {
                    message = String.Format("Sent {0} to the {1} from the top of the deck", ServerHelper.GetPublicCardName(card, originalZone), offset);
                }
                else if (deckPosition == Faust.Andrew.MTG.Player.Shared.DeckPosition.FromBottom)
                {
                    message = String.Format("Sent {0} to the {1} from the bottom of the deck", ServerHelper.GetPublicCardName(card, originalZone), offset);
                }
                else if (deckPosition == Faust.Andrew.MTG.Player.Shared.DeckPosition.Bottom)
                {
                    message = String.Format("Sent {0} to the bottom of the deck", ServerHelper.GetPublicCardName(card, originalZone));
                }
                else if (deckPosition == Faust.Andrew.MTG.Player.Shared.DeckPosition.Top)
                {
                    message = String.Format("Sent {0} to the top of the deck", ServerHelper.GetPublicCardName(card, originalZone));
                }
                else
                {
                    message = String.Format("Sent {0} to a random position of the deck", ServerHelper.GetPublicCardName(card, originalZone));
                }

                Notify(playerId, message, false, Shared.NotificationType.PriorityNotification);

                return true;
            }
        }

        public bool MoveCard(int playerId, int cardId, Shared.Location location)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                Shared.GameCard originalCard = _cards[cardId];                

                if (originalCard.Location.Zone != location.Zone)
                {
                    Notify(playerId, String.Format("Moved {0} to the {1} zone", ServerHelper.GetPublicCardName(originalCard, location.Zone), location.Zone), false, Shared.NotificationType.PriorityNotification);
                }

                Shared.GameCard tempCard = _players[playerId].MoveCard(cardId, location);

                if (tempCard == null) { return false; }                

                //Send the update to all players
                //foreach (PlayerState player in _players.Values)
                //{
                    //if (player.PlayerID == playerId) { continue; }

                Shared.MoveCardAction action = new Faust.Andrew.MTG.Player.Shared.MoveCardAction()
                {
                    PlayerID = playerId,
                    CardID = cardId,
                    Card = tempCard
                };

                Shared.UpdateCardAction updateCardAction = new Faust.Andrew.MTG.Player.Shared.UpdateCardAction()
                {
                    Card = tempCard,
                    PlayerID = playerId,
                    CardID = cardId,
                    IsSystemGenerated = false
                };

                Enqueue(updateCardAction);
                Enqueue(action);

                    
                //}                

                UpdateAllPlayers();

                return true;
            }
        }

        public bool TransferCard(int playerId, int targetPlayerId, int cardId)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                //Get the card from the game state and ensure that the player making the change
                //is the controller of the card, and thus allowed to make the change.
                Shared.GameCard tempCard = _cards[cardId];
                if (tempCard == null || tempCard.ControllerID != playerId)
                {
                    return false;
                }

                _players[tempCard.ControllerID].RemoveCard(tempCard);

                tempCard.ControllerID = targetPlayerId;

                _players[tempCard.ControllerID].AddCard(tempCard);

                //Send the update to all players
                //foreach (Shared.Player player in _players.Values)
                //{
                    Shared.TransferControlAction action = new Faust.Andrew.MTG.Player.Shared.TransferControlAction();
                    action.TargetPlayerID = targetPlayerId;
                    action.CardID = cardId;

                    Enqueue(action);
                //}

                    Notify(playerId, String.Format("Transferred control of " + ServerHelper.GetPublicCardName(tempCard, Shared.Zone.InPlay) + " to {0}", _players[targetPlayerId].Name), false, Shared.NotificationType.PriorityNotification);

                return true;
            }
        }

        private void UpdateAllPlayers()
        {
            foreach (PlayerState player in _players.Values)
            {
                Shared.UpdatePlayerAction action = new Faust.Andrew.MTG.Player.Shared.UpdatePlayerAction();
                action.IsSystemGenerated = true;
                action.PlayerID = 0;
                action.Player = player.CopyClientPlayerState();

                //foreach (int playerToSendTo in _players.Keys)
                //{
                    Enqueue(action);
                //}
            }
        }

        public bool UpdatePlayer(int playerId, Shared.Player player)
        {
            //lock (_lock)
            {
                if (!_gameStarted) { throw CreateFault(new Exception("Game Not Started")); }

                if (playerId != player.PlayerID) { return false; }

                string updateText = String.Empty;
                Shared.Player updatedPlayer = _players[player.PlayerID].UpdatePlayer(player, ref updateText);

                if (updatedPlayer == null)
                {
                    return false;
                }

                //Send the change to all the players
                //foreach (PlayerState tempPlayer in _players.Values)
                //{
                    Shared.UpdatePlayerAction action = new Faust.Andrew.MTG.Player.Shared.UpdatePlayerAction();
                    action.PlayerID = player.PlayerID;
                    action.Player = updatedPlayer;

                    Enqueue(action);
                //}

                    Notify(playerId, updateText, false, Shared.NotificationType.PriorityNotification);

                return true;
            }
        }

        public Shared.GameCard DrawCard(int playerId)
        {
            Shared.GameCard tempCard =_players[playerId].DrawCard();
            Notify(playerId, "Drew a card", false, Shared.NotificationType.PriorityNotification);
            UpdateAllPlayers();
            return tempCard;
        }

        public List<Shared.GameCard> DrawCards(int playerId, int count)
        {
            List<Shared.GameCard> tempCard = _players[playerId].DrawCards(count);
            UpdateAllPlayers();
            return tempCard;
        }

        public bool ShuffleDeck(int playerId)
        {
            Notify(playerId, "Shuffled his/her deck", false, Shared.NotificationType.PriorityNotification);
            _players[playerId].ShuffleDeck();
            return true;
        }

        public int RollDie(int playerId, int sides)
        {
            int value = _rand.Next(1, sides + 1);

            Notify(playerId, String.Format("Rolled a {0} sided die: {1}", sides, value), false, Faust.Andrew.MTG.Player.Shared.NotificationType.Notification);

            return value;
        }

        public void EndTurn(int playerId)
        {
            if (!_gameStarted) { return; }

            if (!_players.ContainsKey(playerId)) { return; }

            PlayerState player = _players[playerId];

            int newOrder = player.Order + 1;
            if (newOrder >= _players.Count) { newOrder = 0; }
            
            var newPlayer = (from tempPlayer in _players.Values
                             where tempPlayer.Order == newOrder
                             select tempPlayer.PlayerID).FirstOrDefault();

            if (_players.ContainsKey(newPlayer))
            {
                Shared.EndTurnAction action = new Faust.Andrew.MTG.Player.Shared.EndTurnAction();
                action.PlayerID = playerId;
                action.TargetPlayerID = newPlayer;
                action.IsSystemGenerated = false;

                Enqueue(action);
            }
            
        }

        public void RandomReveal(int playerId, int count, Shared.Zone sourceZone, List<int> targetPlayers)
        {
            List<Shared.GameCard> randomCards = _players[playerId].GetRandomCards(count, sourceZone);
            //we have our random cards so we reveal them to the targets
            ShowCards(playerId, targetPlayers, randomCards.ToIDList());
        }

        public void RandomDiscard(int playerId, int count, Shared.Zone sourceZone)
        {
            List<Shared.GameCard> randomCards = _players[playerId].GetRandomCards(count, sourceZone);

            Shared.Location location = new Faust.Andrew.MTG.Player.Shared.Location()
            {
                PlayerID = playerId,
                Zone = Faust.Andrew.MTG.Player.Shared.Zone.Graveyard
            };

            //Now we need to move them to the graveyard.
            foreach (Shared.GameCard card in randomCards)
            {
                MoveCard(playerId, card.CardID, location);
            }
        }
        #endregion
    }
}